package com.kerraway.util;

import org.apache.commons.lang3.time.DateUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

public class DateUtil {

  public static final long ONE_MILLIS = 1000L;
  public static final long ONE_DAY = 24 * 3600 * 1000L;
  private static final String yyyy_MM_dd = "yyyy-MM-dd";
  private static final String yyyy_MM_dd_HH = "yyyy-MM-dd-HH";
  private static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";

  public static String format(Date date, String format) {
    if (date == null) {
      return null;
    }
    SimpleDateFormat dateFormat = new SimpleDateFormat(format);
    return dateFormat.format(date);
  }

  public static Date parse(String dateStr, String format) {
    if (dateStr == null) {
      return null;
    }
    try {
      SimpleDateFormat dateFormat = new SimpleDateFormat(format);
      return dateFormat.parse(dateStr);
    } catch (ParseException e) {
      return null;
    }
  }

  /**
   * Please use {@link DateUtil#truncateDay(Date)}.
   *
   * @param date
   * @return Date
   */
  @Deprecated
  public static Date parseDate(Date date) {
    return parse(format(date, yyyy_MM_dd), yyyy_MM_dd);
  }

  public static Date parseDate(String date) {
    return parse(date, yyyy_MM_dd);
  }

  public static String formatDate(Date date) {
    return format(date, yyyy_MM_dd);
  }

  public static Date parseDateTime(String date) {
    return parse(date, yyyy_MM_dd_HH_mm_ss);
  }

  public static String formatDateTime(Date date) {
    return format(date, yyyy_MM_dd_HH_mm_ss);
  }

  public static String formatHHDateTime(Date date) {
    return format(date, yyyy_MM_dd_HH);
  }

  /**
   * Please use {@link DateUtils#addDays(Date, int)}.
   *
   * @param date
   * @return Date
   */
  @Deprecated
  public static Date nextDate(Date date) {
    return nextDate(date, 1);
  }

  /**
   * Please use {@link DateUtils#addDays(Date, int)}.
   *
   * @param date
   * @param n
   * @return Date
   */
  @Deprecated
  public static Date nextDate(Date date, int n) {
    Calendar c = Calendar.getInstance();
    c.setTime(date);
    c.add(Calendar.DATE, n);
    return c.getTime();
  }

  /**
   * Please use {@link DateUtils#addDays(Date, int)} and {@link DateUtils#isSameDay(Date, Date)}.
   *
   * @param date1
   * @param date2
   * @return boolean
   */
  @Deprecated
  public static boolean isNextDate(Date date1, Date date2) {
    if (date1 == null || date2 == null) {
      return false;
    }
    return nextDate(date1).equals(date2);
  }

  /**
   * Calculate days between two dates.
   *
   * @param day1
   * @param day2
   * @return int
   */
  public static int daysBetween(Date day1, Date day2) {
    if (day1 == null || day2 == null) {
      return 0;
    }
    long duration = Math.abs((day2.getTime() - day1.getTime()));
    return (int) TimeUnit.DAYS.convert(duration, TimeUnit.MILLISECONDS);
  }

  /**
   * Ceil two date with specified field, and compare them,
   * if date1 < date2, returns true.
   *
   * @param date1 must not be {@literal null}.
   * @param date2 must not be {@literal null}.
   * @param field {@link Calendar#DATE} etc.
   * @return boolean
   */
  public static boolean before(Date date1, Date date2, int field) {
    Objects.requireNonNull(date1, "date1 must not be null.");
    Objects.requireNonNull(date2, "date2 must not be null.");

    return DateUtils.truncatedCompareTo(date1, date2, field) < 0;
  }

  /**
   * Ceil two date with specified field, and compare them,
   * if date1 > date2, returns true.
   *
   * @param date1 must not be {@literal null}.
   * @param date2 must not be {@literal null}.
   * @param field {@link Calendar#DATE} etc.
   * @return boolean
   */
  public static boolean after(Date date1, Date date2, int field) {
    Objects.requireNonNull(date1, "date1 must not be null.");
    Objects.requireNonNull(date2, "date2 must not be null.");

    return DateUtils.truncatedCompareTo(date1, date2, field) > 0;
  }

  /**
   * Truncate this date, leaving the <code>day</> field.
   *
   * @param date
   * @return Date
   */
  public static Date truncateDay(Date date) {
    Objects.requireNonNull(date, "date must not be null.");

    return DateUtils.truncate(date, Calendar.DATE);
  }

  /**
   * Truncate <code>new Date()<code/>, leaving the <code>day</> field.
   *
   * @return Date
   */
  public static Date today() {
    return truncateDay(new Date());
  }

  /**
   * Get hour in 24h format.
   *
   * @param date
   * @return Integer hour in 24h format
   */
  public static Integer getHourOfDay(Date date) {
    if (date == null) {
      return null;
    }
    Calendar calendar = GregorianCalendar.getInstance();
    calendar.setTime(date);
    return calendar.get(Calendar.HOUR_OF_DAY);
  }

}
